#!/usr/bin/env python
# --coding:utf-8--

from neo4j import GraphDatabase

class neo4jDBOP:
    """ 定义一个neo4j操作类 """
    def __init__(self, neo4j_conf):
        self.driver = GraphDatabase.driver(neo4j_conf["uri"], 
                                           auth=(neo4j_conf["user"], 
                                           neo4j_conf["password"]), 
                                           max_connection_lifetime=3600 * 24 * 30,
                                           keep_alive=True)

    def close(self):
        # Don't forget to close the driver connection when you are finished with it
        self.driver.close()

    def insertFloder(self, fully_qualified_name, striped_name):
        """ 插入文件系统目录节点
        """
        query = (
            "merge (fnode:Folder{fully_qualified_name:$fully_qualified_name, striped_name:$striped_name}) "
            "return fnode.fully_qualified_name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, fully_qualified_name=fully_qualified_name, striped_name=striped_name)
                tx.commit()
        # self.driver.close()

        return result
    
    def insertPackage(self, fully_qualified_name, striped_name):
        """ 包含__init__.py的python 作为Package节点存入数据库
        """
        query = (
            "merge (node:Package{fully_qualified_name:$fully_qualified_name, striped_name:$striped_name}) "
            "return node.fully_qualified_name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, fully_qualified_name=fully_qualified_name, striped_name=striped_name)
                tx.commit()
        # self.driver.close()

        return result
    
    def insertModule(self, fully_qualified_name, striped_name):
        """ python file作为Module节点存入数据库
        """
        # print("insertModule: ", fully_qualified_name)
        query = (
            "merge (module_node:Module{fully_qualified_name:$fully_qualified_name, striped_name:$striped_name, ast_error:False}) "
            "return module_node.fully_qualified_name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, fully_qualified_name=fully_qualified_name, striped_name=striped_name)
                tx.commit()
        # self.driver.close()

        return result
    
    def insertClass(self, fully_qualified_name, striped_name, bases=None):
        """ 插入class节点
        """
        # print("insertClass: ", fully_qualified_name)
        query = (
            "merge (node:Class{fully_qualified_name:$fully_qualified_name, striped_name:$striped_name}) "
            "return node.fully_qualified_name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, striped_name=striped_name, fully_qualified_name=fully_qualified_name)
                tx.commit()
        # self.driver.close()

        return result
    
    def insertFunction(self, fully_qualified_name, striped_name, args):
        """ 插入function节点
        """
        # print("insertFunction: ", fully_qualified_name)
        arg_names = args[0]
        arg_defaults = args[1]
        query = (
            "merge (node:Function{fully_qualified_name:$fully_qualified_name, striped_name:$striped_name, args:$args, defaults:$defaults}) "
            "return node.fully_qualified_name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, striped_name=striped_name, fully_qualified_name=fully_qualified_name, args=arg_names, defaults=arg_defaults)
                tx.commit()
        # self.driver.close()

        return result
    
    def insertFileSystemEdge(self, father_fully_qualified_name, child_fully_qualified_name):
        """ 在文件系统父子节点之间建立关系
        """
        query = (
            "match (father_node{fully_qualified_name:$father_fully_qualified_name}) "
            "match (child_node{fully_qualified_name:$child_fully_qualified_name}) "
            "merge (father_node) -[fs_child:fs_child]-> (child_node) "
            "merge (child_node) -[fs_father:fs_father]-> (father_node) "
            "return father_node, child_node"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, father_fully_qualified_name=father_fully_qualified_name, child_fully_qualified_name=child_fully_qualified_name)
                tx.commit()
        # self.driver.close()

        return result
    
    # def insertFileSystemEdgeFather(self, source_path, dest_path):
    #     query = (
    #         "merge (fnode1:FileSystem{fully_qualified_name:$source_path}) "
    #         "merge (fnode2:FileSystem{fully_qualified_name:$dest_path}) "
    #         "merge (fnode1) -[fs_father:fs_father]-> (fnode2) "
    #         "return fnode1, fnode2"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, source_path=source_path, dest_path=dest_path)
    #             tx.commit()
    #     # self.driver.close()

    #     return result

    def insertPackageContains(self, father_fully_qualified_name, package_fully_qualified_name):
        """ 在folder/package间添加package contains关系
        """
        query = (
            "match (father_node{fully_qualified_name:$father_fully_qualified_name}) "
            "match (node{fully_qualified_name:$package_fully_qualified_name}) "
            "merge (father_node) -[:package_contains]-> (node) "
            "return father_node, node"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, father_fully_qualified_name=father_fully_qualified_name, package_fully_qualified_name=package_fully_qualified_name)
                tx.commit()
        # self.driver.close()

        return result
    
    def insertModuleContains(self, father_fully_qualified_name, module_fully_qualified_name):
        """ 在folder/module、module/module间添加module contains关系
        """
        query = (
            "match (father_node{fully_qualified_name:$father_fully_qualified_name}) "
            "match (module_node{fully_qualified_name:$module_fully_qualified_name}) "
            "merge (father_node) -[:module_contains]-> (module_node) "
            "return father_node, module_node"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, father_fully_qualified_name=father_fully_qualified_name, module_fully_qualified_name=module_fully_qualified_name)
                tx.commit()
        # self.driver.close()

        return result
    
    def insertClassContains(self, father_fully_qualified_name, class_fully_qualified_name):
        """ 在module/class间添加class contains关系
        """
        query = (
            "match (father_node{fully_qualified_name:$father_fully_qualified_name}) "
            "match (class_node{fully_qualified_name:$class_fully_qualified_name}) "
            "merge (father_node) -[:class_contains]-> (class_node) "
            "return father_node, class_node"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, father_fully_qualified_name=father_fully_qualified_name, class_fully_qualified_name=class_fully_qualified_name)
                tx.commit()
        # self.driver.close()

        return result
    
    def insertFunctionContains(self, father_fully_qualified_name, function_fully_qualified_name):
        """ 在class/function、module/function间添加function contains关系
        """
        query = (
            "match(father_node{fully_qualified_name:$father_fully_qualified_name}) "
            "match (function_node{fully_qualified_name:$function_fully_qualified_name}) "
            "merge (father_node) -[contains:function_contains]-> (function_node) "
            "return father_node.fully_qualified_name, function_node.fully_qualified_name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, father_fully_qualified_name=father_fully_qualified_name, function_fully_qualified_name=function_fully_qualified_name)
                tx.commit()
        # self.driver.close()

        return result

    # def insertASTedge(self, class_name, fun_name):
    #     """ """
    #     query = (
    #         "merge (classnode:class{fully_qualified_name:$class_name}) "
    #         "merge (funnode:Function{fully_qualified_name:$fun_name}) "
    #         "merge (classnode) -[contains:fun_contains]-> (funnode) "
    #         "return classnode.fully_qualified_name, funnode.fully_qualified_name"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, class_name=class_name, fun_name=fun_name)
    #             tx.commit()
    #     # self.driver.close()

    #     return result
    

    # def insertFileFunEdge(self, path, fun_fully_qualified_name):
    #     query = (
    #         "merge (fnode:FileSystem{fully_qualified_name:$path}) "
    #         "merge (node:Function{fully_qualified_name:$fun_name}) "
    #         "merge (fnode) -[contains:fun_contains]-> (node) "
    #         "return fnode.fully_qualified_name, node.fully_qualified_name"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, path=path, fun_name=fun_fully_qualified_name)
    #             tx.commit()
    #     # self.driver.close()

    #     return result
    
    # def insertFileClassEdge(self, path, class_fully_qualified_name):
    #     query = (
    #         "merge (fnode:FileSystem{fully_qualified_name:$path}) "
    #         "merge (node:class{fully_qualified_name:$class_name}) "
    #         "merge (fnode) -[contains:class_contains]-> (node) "
    #         "return fnode.fully_qualified_name, node.fully_qualified_name"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, path=path, class_name=class_fully_qualified_name)
    #             tx.commit()
    #     # self.driver.close()

    #     return result
    
    def insertPypiPackage(self, name, version, downloaded):
        pkg_id = name + "@" + version
        query = (
            "merge (pkg:PypiPackage{id:$pkg_id, name:$name, version:$version, downloaded:$downloaded, analysed:false}) "
            "return pkg"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, pkg_id=pkg_id, name=name, version=version, downloaded=downloaded)
                tx.commit()
        # self.driver.close()

        return result
    
    # def checkPkgExist(self, name, version):
    #     pkg_id = name + "@" + version
    #     query = (
    #         "match (pkg:Package{id:$pkg_id}) "
    #         "return pkg"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, pkg_id=pkg_id, name=name, version=version)
    #             tx.commit()
    #     # self.driver.close()

    #     return result
    
    def checkPkgDownloaded(self, name, version):
        pkg_id = name + "@" + version
        query = (
            "match (pkg:PypiPackage{id:$pkg_id}) "
            "return pkg.downloaded as downloaded"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, pkg_id=pkg_id, name=name, version=version)
                result = [record['downloaded'] for record in result]
                tx.commit()
        # self.driver.close()

        return result
    
    def checkPkgAnalysed(self, name, version):
        pkg_id = name + "@" + version
        query = (
            "match (pkg:PypiPackage{id:$pkg_id}) "
            "return pkg.analysed as analysed"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, pkg_id=pkg_id, name=name, version=version)
                result = [record['analysed'] for record in result]
                tx.commit()
        # self.driver.close()

        return result
    
    def labelPkgDownloaded(self, name, version):
        pkg_id = name + "@" + version
        query = (
            "match (pkg:PypiPackage{id:$pkg_id}) "
            "set pkg.downloaded=true"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, pkg_id=pkg_id)
                tx.commit()
        # self.driver.close()

        return result

    # def labelPkgAlldependencies(self, name, version):
    #     pkg_id = name + "@" + version
    #     query = (
    #         "match (pkg:Package{id:$pkg_id}) "
    #         "set pkg.all_dependencies=true"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, pkg_id=pkg_id)
    #             tx.commit()
    #     # self.driver.close()

    #     return result
    
    def labelPkgAnalysed(self, name, version):
        pkg_id = name + "@" + version
        query = (
            "match (pkg:PypiPackage{id:$pkg_id}) "
            "set pkg.analysed=true"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, pkg_id=pkg_id)
                tx.commit()
        # self.driver.close()

        return result
    
    def labelModuleASTerror(self, fully_qualified_name, striped_name):
        """ 标记python file ast解析出错
        """
        query = (
            "match (module_node:Module{fully_qualified_name:$fully_qualified_name, striped_name:$striped_name}) "
            "set module_node.ast_error=true"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, fully_qualified_name=fully_qualified_name, striped_name=striped_name)
                tx.commit()
        # self.driver.close()

        return result
    
    # def labelPkgASTerror(self, name, version):
    #     pkg_id = name + "@" + version
    #     query = (
    #         "match (pkg:Package{id:$pkg_id}) "
    #         "set pkg.ast_error=true"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, pkg_id=pkg_id)
    #             tx.commit()
    #     # self.driver.close()

    #     return result
    
    # def getFunctionInfo(self, fully_qualified_name):
    #     query = (
    #         "match (fun_node:Function{fully_qualified_name:$fully_qualified_name}) "
    #         "return fun_node.args as args, fun_node.defaults as defaults"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, fully_qualified_name=fully_qualified_name)
    #             result = [(record['args'], record['defaults']) for record in result]
    #             tx.commit()

    #     # self.driver.close()
    #     return result
    
    # def searchImport(self, pkg, name):
    #     # print("searchImport: ", pkg, name)
    #     query = (
    #         "match (root:FileSystem{fully_qualified_name:$root_path}) "
    #         "-[r:fs_child*0..]-> (path_node:FileSystem{striped_name:$name}) "
    #         "return path_node.fully_qualified_name as path, path_node.striped_name as name"
    #     )
    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, root_path=pkg, name=name)
    #             result = [(record['path'], record['name']) for record in result]
    #             tx.commit()

    #     # self.driver.close()
    #     return result  

    # def searchImportFrom(self, pkg, path, name):
    #     # print("DBOP searchIm portFrom: ", pkg, path, name)
    #     """ pkg是当前package名，用于查找neo4j中文件系统根节点
    #         path是文件系统路径，为list
    #         name是API名，是(name, asname)形式
    #     """
    #     # print(root.path, root.striped_name)
    #     # 找到root节点下，path起始节点
    #     # print("***: ", pkg, path, name)
    #     query = (
    #         "match (root:FileSystem{fully_qualified_name:$root_path}) "
    #         "-[r:fs_child*0..]-> (path_node:FileSystem{striped_name:$path_name}) "
    #         "return path_node.fully_qualified_name as path, path_node.striped_name as name"
    #     )
    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             root_result = tx.run(query, root_path=pkg, path_name=path[0])
    #             root_result = [(record['path'], record['name']) for record in root_result]
    #             tx.commit()

    #     # print("root_result: ", root_result)
    #     result = []
    #     for pre_node in root_result:
    #         for i in range(1, len(path)):
    #             # print(path[i], path[i + 1])
    #             query = (
    #                 "match (pre_node:FileSystem{fully_qualified_name:$pre_node_path, striped_name:$pre_node_name}) "
    #                 "-[r:fs_child]-> (path_node:FileSystem{striped_name:$path_name}) "
    #                 "return path_node.fully_qualified_name as path, path_node.striped_name as name"
    #             )
            #     with self.driver.session() as session:
            #         with session.begin_transaction() as tx:
            #             result = tx.run(query, pre_node_path=pre_node[0], pre_node_name=pre_node[1], path_name=path[i])
            #             result = [(record['path'], record['name']) for record in result]
            #             tx.commit()
            #             # print("result ", i, ": ", result)
            #             if len(result) > 0:
            #                 pre_node = result[0]
            #                 # print("pre_node: ", pre_node)
            #             else:
            #                 return []
            
            # # print("* pre_node: ", pre_node)
            # # for API_name in name:
            # # print("API_name: ", name)
            # query = (
            #         "match (pre_node:FileSystem{fully_qualified_name:$pre_node_path, striped_name:$pre_node_name}) "
            #         "-[:class_contains | :fun_contains | :module_contains]-> (api_node{striped_name:$api_name}) "
            #         "return api_node.fully_qualified_name as fully_qualified_name, api_node.type as type"
            #     )
            # with self.driver.session() as session:
            #     with session.begin_transaction() as tx:
            #         result = tx.run(query, pre_node_path=pre_node[0], pre_node_name=pre_node[1], api_name=name[0])
            #         result = [(record['fully_qualified_name'], record['type']) for record in result]
        #             tx.commit()
        #     # print("result: ", result)
        #     # print()
        #     if len(result) > 0:
        #         break
    
        # # self.driver.close()
        # # print("final result: ", result)
        # # print()
        # return result
    
    def mappingImport(self, old_name, new_name):
        """ 实现import映射关系
            old_name和new_name都是用fully_qualified_name表示
        """
        if old_name == new_name:
            return
        query = (
            "match (new_node{fully_qualified_name:$new_name}) "
            "match (old_node{fully_qualified_name:$old_name}) "
            "merge (new_node) -[:import_mapping]-> (old_node) "
            "merge (old_node) -[:import_mapping]-> (new_node)"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, new_name=new_name, old_name=old_name)
                tx.commit()

        # self.driver.close()
        return
    
    # def mappingImportClass(self, old_name, new_name):
    #     """ 实现function的映射关系
    #         old_api是pkg中原始定义的API，用fully_qualified_name表示
    #         new_api是import新产生的API，用fully_qualified_name表示
    #     """

    #     query = (
    #         "match (new_node{fully_qualified_name:$new_name}) "
    #         "match (old_node{fully_qualified_name:$old_name}) "
    #         "merge (new_node) -[class_mapping:class_mapping]-> (old_node)"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, new_name=new_name, old_name=old_name)
    #             tx.commit()

    #     # self.driver.close()
    #     return result
    
    # def mappingImportModule(self, old_name, new_name):
    #     """ 实现module之间的映射关系
    #         old_module和new_module用path表示
    #     """

    #     query = (
    #         "match (new_module{fully_qualified_name:$new_name}) "
    #         "match (old_module{fully_qualified_name:$old_name}) "
    #         "merge (new_module) -[module_mapping:module_mapping]-> (old_module)"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, new_name=new_name, old_name=old_name)
    #             tx.commit()

    #     # self.driver.close()
    #     return result
    
    # def mappingImportPackage(self, old_name, new_name):
    #     """ 实现Package之间的映射关系
    #         old_name和new_name用path表示
    #     """

    #     query = (
    #         "match (new_node{fully_qualified_name:$new_name}) "
    #         "match (old_node{fully_qualified_name:$old_name}) "
    #         "merge (new_node) -[package_mapping:package_mapping]-> (old_node)"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, new_name=new_name, old_name=old_name)
    #             tx.commit()

    #     # self.driver.close()
    #     return result
    
    # def searchAPI(self, root_file, path):
    #     query = (
    #         "match (root:FileSystem{fully_qualified_name:$root_path}) "
    #         "-[r:fs_child*0..]-> (path_node{striped_name:$path_name}) "
    #         "return path_node.fully_qualified_name as fully_qualified_name, path_node.striped_name as name"
    #     )
    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             root_result = tx.run(query, root_path=root_file, path_name=path[0])
    #             root_result = [(record['fully_qualified_name'], record['name']) for record in root_result]
    #             tx.commit()

    #     # print("root_result: ", root_result)
    #     result = []
    #     for pre_node in root_result:
    #         for i in range(1, len(path)):
    #             # print(path[i], path[i + 1])
    #             query = (
    #                 "match (pre_node{fully_qualified_name:$fully_qualified_name, striped_name:$pre_node_name}) "
    #                 "-[:fs_child | :class_contains | :function_contains]-> (path_node:{striped_name:$path_name}) "
    #                 "return path_node.fully_qualified_name, path_node.striped_name as name"
    #             )
    #             with self.driver.session() as session:
    #                 with session.begin_transaction() as tx:
    #                     result = tx.run(query, pre_node_name=pre_node, path_name=path[i])
    #                     result = [(record['fully_qualified_name'], record['name']) for record in result]
    #                     tx.commit()
    #                     # print("result ", i, ": ", result)
    #                     if len(result) > 0:
    #                         pre_node = result[0]
    #                         # print("pre_node: ", pre_node)
    #                     else:
    #                         return []
    #         if len(result) > 0:
    #             break

    #     # self.driver.close()
    #     return result

    # def searchBase(self, file_path, base_name):
    # def searchFullyQualifiedName(self, fully_qualified_name):

    def searchPath(self, prefix, path):
        """ 根据pkg和path来定位节点
        """
        # print("prefix: ", prefix)
        # print("path: ", path)
        query = (
            "match (node) where node.fully_qualified_name STARTS WITH $prefix and node.fully_qualified_name ENDS WITH $path "
            "return node.fully_qualified_name as fully_qualified_name, node.striped_name as name, labels(node) as type"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, prefix=prefix, path=path)
                result = [(record['fully_qualified_name'], record['name'], record['type'][0]) for record in result]
                tx.commit()

        # print("result: ", result)
        # print()
        
        # self.driver.close()
        return result
    
    def searchFunction(self, fully_qualified_name):
        """ 获取module/calss包含的所有function
            返回为[(function_fully_qualified_name, function_striped_name)]
        """
        # print("class_name: ", class_name)
        query = (
            "match (node{fully_qualified_name:$name}) -[:function_contains]-> (function_node) "
            "return function_node.fully_qualified_name as fully_qualified_name, function_node.striped_name as name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, name=fully_qualified_name)
                result = [(record['fully_qualified_name'], record['name']) for record in result]
                tx.commit()

        # self.driver.close()
        return result
    
    # def searchModuleFunction(self, module_name):
    #     """ 获取module包含的所有function
    #         module name为fully_qualified_name
    #         返回为[(function_fully_qualified_name, function_striped_name)]
    #     """
    #     # print("class_name: ", class_name)
    #     query = (
    #         "match (module_node{fully_qualified_name:$module_name}) -[:function_contains]-> (function_node) "
    #         "return function_node.fully_qualified_name as fully_qualified_name, function_node.striped_name as name"
    #     )

    #     with self.driver.session() as session:
    #         with session.begin_transaction() as tx:
    #             result = tx.run(query, module_name=module_name)
    #             result = [(record['fully_qualified_name'], record['name']) for record in result]
    #             tx.commit()

    #     # self.driver.close()
    #     return result
    
    def searchClass(self, fully_qualified_name):
        """ 获取module包含的所有class
            返回[(class_fully_qualified_name, class_striped_name)]
        """
        
        query = (
            "match (node{fully_qualified_name:$name}) -[:class_contains]-> (class_node) "
            "return class_node.fully_qualified_name as fully_qualified_name, class_node.striped_name as name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, name=fully_qualified_name)
                result = [(record['fully_qualified_name'], record['name']) for record in result]
                tx.commit()

        # self.driver.close()
        return result
    
    def searchModule(self, fully_qualified_name):
        """ 获取package/module包含的所有module
            返回[(module_fully_qualified_name, module_striped_name)]
        """
        # print("package_name: ", package_name)
        query = (
            "match (node{fully_qualified_name:$name}) -[:module_contains]-> (module_node) "
            "return module_node.fully_qualified_name as fully_qualified_name, module_node.striped_name as name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, name=fully_qualified_name)
                result = [(record['fully_qualified_name'], record['name']) for record in result]
                tx.commit()

        # self.driver.close()
        return result
    
    def searchPackage(self, fully_qualified_name):
        """ 获取package/module中包含的所有package 
            返回[(package_fully_qualified_name, package_striped_name)]
        """

        query = (
            "match (node{fully_qualified_name:$name}) -[:package_contains]-> (package_node) "
            "return package_node.fully_qualified_name as fully_qualified_name, package_node.striped_name as name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, name=fully_qualified_name)
                result = [(record['fully_qualified_name'], record['name']) for record in result]
                tx.commit()

        # self.driver.close()
        return result

    def searchFunctionInfo(self, fully_qualified_name):
        """ 查找function的参数信息
        """
        query = (
            "match (node:Function{fully_qualified_name:$fully_qualified_name}) "
            "return node.args as args, node.defaults as defaults"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, fully_qualified_name=fully_qualified_name)
                result = [(record['args'], record['defaults']) for record in result]
                tx.commit()

        # self.driver.close()
        return result

    def searchAPI(self, fully_qualified_name):
        """ 根据path来定位API
        """
        # print("prefix: ", prefix)
        # print("path: ", path)
        query = (
            # "match (node) where node.fully_qualified_name ENDS WITH $path "
            "match (node{fully_qualified_name:$fully_qualified_name}) "
            "return node.fully_qualified_name as fully_qualified_name, node.striped_name as name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, fully_qualified_name=fully_qualified_name)
                result = [(record['fully_qualified_name'], record['name']) for record in result]
                tx.commit()

        # print("result: ", result)
        # print()
        
        # self.driver.close()
        return result

    def insertFilesystemContains(self, pkg_id, fully_qualified_name):
        """ 在PypiPackage节点和文件系统根节点之间添加关系
            pkg_id为pkg_name@version
            fully_qualified_name为根节点的路径，为pkg_name-version
        """

        query = (
            "match (node:PypiPackage{id:$pkg_id}) "
            "match (fs_node{fully_qualified_name:$fully_qualified_name}) "
            "merge node -[fs:Filesystem]-> fs_node"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, pkg_id=pkg_id, fully_qualified_name=fully_qualified_name)
                tx.commit()
        
        # self.driver.close()
        return result

    def insertCallRelation(self, caller, callee):
        """ 在函数之间添加调用关系
            caller和callee都用fully_qualified_name表示
        """

        query = (
            "match (caller{fully_qualified_name:$caller}) "
            "match (callee{fully_qualified_name:$callee}) "
            "merge caller -[call:call]-> callee "
            "merge callee -[called:called]-> caller"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, caller=caller, callee=callee)
                tx.commit()
        
        # self.driver.close()
        return result
    
    def insertInheritRelation(self, father, child):
        """ 在class之间添加继承关系
            father和child都用fully_qualified_name表示
        """

        query = (
            "match (father{fully_qualified_name:$father}) "
            "match (child{fully_qualified_name:$child}) "
            "merge (father) -[inherited:inherited]-> (child) "
            "merge (child) -[inherit:inherit]-> (father)"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, father=father, child=child)
                tx.commit()
        
        # self.driver.close()
        return result
    
    def checkFunction(self, fully_qualified_name):
        """ 查询某个function是否存在
        """

        query = (
            "match (node{fully_qualified_name:$fully_qualified_name}) "
            "return node.fully_qualified_name as fully_qualified_name, node.striped_name as name"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, fully_qualified_name=fully_qualified_name)
                result = [(record['fully_qualified_name'], record['name']) for record in result]
                tx.commit()
        
        # self.driver.close()
        return result
    
    def mappingInherit(self, father_name, child_name):
        """ 在子类和父类有继承关系的函数之间添加mapping
            father_name和child_name都用fully_qualified_name表示
        """
        
        query = (
            "match (father_node{fully_qualified_name:$father_name}) "
            "match (child_node{fully_qualified_name:$child_name}) "
            "merge (father_node) -[:inherit_mapping]-> (child_node) "
            "merge (child_node) -[:inherit_mapping]-> (father_node)"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query, father_name=father_name, child_name=child_name)
                tx.commit()

        # self.driver.close()
        return
    
    def getNodeCount(self):
        """ 获取neo4j数据库的节点数量
        """

        query = (
            "match (node) return count(*) as count"
        )

        with self.driver.session() as session:
            with session.begin_transaction() as tx:
                result = tx.run(query)
                result = [record['count'] for record in result]
                tx.commit()

        return result